// 单气泡：连接中 → 思考中 → 已完成思考（可折叠 <think> 原文）+ 同气泡内显示正式回答（虚线分隔）
// 且：思考区默认展开；增量追加时自动下滑到页面底部（需要 WXML 里有 id="bottomA" 和 id="bottomB" 两个锚点）

const SHOW_REASONING_DEV = true; // 上线可改为 false（仅影响点击折叠是否生效，不影响默认展开）

type ReasoningMsg = {
  id: string;
  role: 'assistant';
  kind: 'reasoning';
  status: 'connecting' | 'thinking' | 'done';
  text: string;    // <think> 原文（思考区）
  answer: string;  // 正式回复（同气泡底部）
  open: boolean;   // 折叠开关：默认 true（展开）
  like?: boolean;
  dislike?: boolean;
  canRetry?: boolean;
  sourceUserText?: string;
};

type ChatMsg =
  | {
      id: string;
      role: 'user' | 'assistant';
      text: string;
      kind?: 'msg';
      like?: boolean;
      dislike?: boolean;
      canRetry?: boolean;         // 这条回答支持重试（默认 true）
      sourceUserText?: string;    // 这条回答对应的用户原始提问
    }
  | {
      id: string;
      role: 'assistant';
      kind: 'status';
      status: 'connecting' | 'thinking';
    }
  | ReasoningMsg;

type PageData = {
  statusBarHeight: number;
  draft: string;
  suggestions: string[];
  messages: ChatMsg[];
  scrollToId: string; // 绑定 scroll-view 的 scroll-into-view
};

type PageCustom = {
  onBack(): void;
  onShuffle(): void;
  onPick(e: WechatMiniprogram.TouchEvent): void;
  onInput(e: WechatMiniprogram.Input): void;
  onSend(): void;

  appendUser(text: string): void;

  streamAsk(userText: string): void;
  consumeStreamText(text: string): void;
  processEvent(evt: { event?: string; answer?: string }): void;

  // reasoning 单气泡
  createReasoningBubble(userText: string): void;
  setReasoningStatus(next: 'connecting' | 'thinking' | 'done'): void;
  appendReasoningText(delta: string): void;
  appendAnswerInReasoning(delta: string): void;
  finishReasoning(): void;
  onToggleReasoning(e: WechatMiniprogram.TouchEvent): void;

  // 兜底：完全无 think 的直出回答
  upsertAssistantChunk(chunk: string): void;

  // 工具
  handleAnswerDelta(delta: string): void;
  normalizeThinkTags(s: string): string;
  cleanAnswer(raw: string): string;
  ab2utf8(buf: ArrayBuffer): string;

  // 自动下滑
  _scrollToBottom(): void;

  // 操作条事件
  onCopy(e: WechatMiniprogram.TouchEvent): void;
  onLike(e: WechatMiniprogram.TouchEvent): void;
  onDislike(e: WechatMiniprogram.TouchEvent): void;
  onRetry(e: WechatMiniprogram.TouchEvent): void;
  reportFeedback(msgId: string, action: 'like' | 'dislike'): void;

  // 内部状态
  _buf: string;
  _mode: 'sse';
  _sending: boolean;
  _req: WechatMiniprogram.RequestTask | null;
  _lastSendText: string;
  _lastSendAt: number;

  _inReasoning: boolean;       // 是否在 <think> 段内
  _reasoningId: string | null; // 当前 reasoning 气泡 id

  _flipToA: boolean;           // A/B 锚点切换
  _scrollTicking: boolean;     // 简易节流
};

Page<PageData, PageCustom>({
  data: {
    statusBarHeight: 20,
    draft: '',
    suggestions: ['高血压是什么', '如何预防高血压', '高血压在饮食上要注意什么？'],
    messages: [] as ChatMsg[],
    scrollToId: 'bottomA',
  },

  _buf: '',
  _mode: 'sse',
  _sending: false,
  _req: null,
  _lastSendText: '',
  _lastSendAt: 0,

  _inReasoning: false,
  _reasoningId: null,

  _flipToA: false,
  _scrollTicking: false,

  onLoad() {
    try {
      const sys = wx.getSystemInfoSync();
      this.setData({ statusBarHeight: (sys as any)?.statusBarHeight || 20 });
    } catch { this.setData({ statusBarHeight: 20 }); }
  },

  onUnload() {
    if (this._req && typeof this._req.abort === 'function') { try { this._req.abort(); } catch {} }
    this._sending = false; this._req = null;
  },

  onBack() { wx.navigateBack(); },

  onShuffle() {
    const pool = [
      '我今天血压有点高怎么办？','血压药能不能停？','家庭如何正确测血压？','什么是单纯收缩期高血压？','吃盐多少算超标？',
    ];
    const arr = pool.sort(() => Math.random() - 0.5).slice(0, 3);
    this.setData({ suggestions: arr });
  },

  onPick(e: WechatMiniprogram.TouchEvent) {
    const q = ((e.currentTarget as any)?.dataset?.q as string) || '';
    this.setData({ draft: q });
  },

  onInput(e: WechatMiniprogram.Input) {
    this.setData({ draft: (e?.detail?.value as string) || '' });
  },

  onSend() {
    if (this._sending) return;
    const text = (this.data.draft || '').trim();
    if (!text) return;

    const now = Date.now();
    if (text === this._lastSendText && (now - this._lastSendAt) < 1000) return;
    this._lastSendText = text; this._lastSendAt = now;

    this._sending = true; this._buf = '';
    this._inReasoning = false; this._reasoningId = null;
    this.setData({ draft: '' });

    this.appendUser(text);
    this.createReasoningBubble(text); // 初始 connecting，并记录 sourceUserText
    this.streamAsk(text);
  },

  appendUser(text: string) {
    const id = 'u_' + Date.now();
    this.setData({ messages: this.data.messages.concat([{ id, role: 'user', text }]) });
    this._scrollToBottom();
  },

  // —— 仅“完全无 think”时才会落到普通 assistant —— 
  upsertAssistantChunk(chunk: string) {
    const msgs = this.data.messages.slice();
    const last = msgs[msgs.length - 1] as any;
    if (!msgs.length || last?.role !== 'assistant' || last?.kind) {
      msgs.push({
        id: 'a_' + Date.now(),
        role: 'assistant',
        text: '',
        kind: 'msg',
        canRetry: true,
        sourceUserText: this._lastSendText,
        like: false,
        dislike: false
      } as ChatMsg);
    }
    (msgs[msgs.length - 1] as any).text += (chunk || '');
    this.setData({ messages: msgs });
    this._scrollToBottom();
  },

  // —— 创建 reasoning 单气泡 —— 
  createReasoningBubble(userText: string) {
    if (this._reasoningId) return;
    const id = 'rt_' + Date.now();
    const msg: ReasoningMsg = {
      id, role: 'assistant', kind: 'reasoning',
      status: 'connecting',
      text: '', answer: '',
      open: true,
      canRetry: false,          // 生成完前禁用
      like: false,
      dislike: false,
      sourceUserText: userText,
    };
    this._reasoningId = id;
    this.setData({ messages: this.data.messages.concat([msg]) });
    this._scrollToBottom();
  },

  setReasoningStatus(next: 'connecting' | 'thinking' | 'done') {
    if (!this._reasoningId) return;
    const msgs = this.data.messages.slice();
    const idx = msgs.findIndex(m => (m as any).id === this._reasoningId);
    if (idx >= 0) {
      (msgs[idx] as ReasoningMsg).status = next;
      this.setData({ messages: msgs });
      this._scrollToBottom();
    }
  },

  appendReasoningText(delta: string) {
    if (!this._reasoningId || !delta) return;
    const msgs = this.data.messages.slice();
    const idx = msgs.findIndex(m => (m as any).id === this._reasoningId);
    if (idx >= 0) {
      (msgs[idx] as ReasoningMsg).text += delta;
      this.setData({ messages: msgs });
      this._scrollToBottom();
    }
  },

  appendAnswerInReasoning(delta: string) {
    if (!this._reasoningId || !delta) return;
    const msgs = this.data.messages.slice();
    const idx = msgs.findIndex(m => (m as any).id === this._reasoningId);
    if (idx >= 0) {
      (msgs[idx] as ReasoningMsg).answer += delta;
      this.setData({ messages: msgs });
      this._scrollToBottom();
    }
  },

  finishReasoning() {
    if (!this._reasoningId) return;
    const msgs = this.data.messages.slice();
    const idx = msgs.findIndex(m => (m as any).id === this._reasoningId);
    if (idx >= 0) {
      const m = msgs[idx] as ReasoningMsg;
      m.status = 'done';
      m.canRetry = true; // ✅ 生成完毕后允许“重试”
      this.setData({ messages: msgs });
      this._scrollToBottom();
    } else {
      this.setReasoningStatus('done'); // 兜底
    }
  },

  onToggleReasoning(e: WechatMiniprogram.TouchEvent) {
    if (!SHOW_REASONING_DEV) return;
    const id = (e.currentTarget as any).dataset.id;
    const msgs = this.data.messages.slice();
    const idx = msgs.findIndex((m: any) => m.id === id);
    if (idx >= 0 && (msgs[idx] as ReasoningMsg).kind === 'reasoning') {
      (msgs[idx] as ReasoningMsg).open = !(msgs[idx] as ReasoningMsg).open;
      this.setData({ messages: msgs });
      this._scrollToBottom();
    }
  },

  streamAsk(userText: string) {
    if (this._req && typeof this._req.abort === 'function') { try { this._req.abort(); } catch {} }

    const reqTask = wx.request({
      url: 'http://localhost:7001/ai/chat/stream', // 换成你的 HTTPS 合法域名
      method: 'POST',
      enableChunked: true,
      responseType: 'arraybuffer',
      header: {
        'Content-Type': 'application/json; charset=utf-8',
        'Accept': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive',
      },
      data: { query: userText },

      success: (res) => {
        try {
          const text = (res.data instanceof ArrayBuffer) ? this.ab2utf8(res.data) : String(res.data || '');
          if (text) this.handleAnswerDelta(text);
        } catch {}
      },
      fail: (err) => {
        const msg = (err as any)?.errMsg || '请求失败';
        this.appendAnswerInReasoning('\n[网络错误] ' + msg);
        wx.showToast({ title: '网络错误', icon: 'none' });
        this._sending = false; this._req = null;
      },
      complete: () => {
        if (this._sending && !(this as any)._waitingMore) { this._sending = false; this._req = null; }
      },
    });

    this._req = reqTask;

    const anyTask = reqTask as unknown as {
      onChunkReceived?: (cb: (res: { data: ArrayBuffer }) => void) => void;
      onHeadersReceived?: (cb: (res: { header: Record<string, string> }) => void) => void;
    };

    if (anyTask?.onChunkReceived) {
      anyTask.onChunkReceived((res) => {
        const text = this.ab2utf8(res.data);
        this.consumeStreamText(text);
        (this as any)._waitingMore = true;
      });
    }
    if (anyTask?.onHeadersReceived) {
      anyTask.onHeadersReceived((_res) => {
        this._mode = 'sse';
        this.setReasoningStatus('thinking'); // 连接成功 → 思考中
      });
    }
  },

  consumeStreamText(chunkText: string) {
    this._buf = (this._buf || '') + (chunkText || '');
    const parts = this._buf.split(/\r?\n/);
    this._buf = parts.length ? (parts[parts.length - 1] || '') : '';

    for (let i = 0; i < parts.length - 1; i++) {
      const line = parts[i] || '';
      if (!line) continue;
      if (line.indexOf('data:') !== 0) continue;

      const payload = line.slice(5).replace(/^\s+/, '');
      if (!payload) continue;

      try {
        const evt = JSON.parse(payload) as { event?: string; answer?: string };
        this.processEvent(evt);
      } catch { /* 确保后端为 JSON SSE */ }
    }
  },

  processEvent(evt: { event?: string; answer?: string }) {
    if (!evt) return;

    if (evt.event === 'message' && typeof evt.answer === 'string') {
      this.handleAnswerDelta(evt.answer);
      return;
    }

    if (evt.event === 'done') {
      this.finishReasoning();
      this._sending = false; this._req = null; (this as any)._waitingMore = false;
      return;
    }

    if (evt.event === 'error' && typeof evt.answer === 'string') {
      this.appendAnswerInReasoning('\n[服务错误] ' + evt.answer);
      this._sending = false; this._req = null; (this as any)._waitingMore = false;
      return;
    }
  },

  // —— 核心：把 think 和回答都写到同一个 reasoning 气泡 —— 
  handleAnswerDelta(delta: string) {
    let s = this.normalizeThinkTags(String(delta || ''));

    // 确保有 reasoning 气泡（初始 onSend 已创建，这里兜底；以及重试逻辑也会设置 _reasoningId）
    if (!this._reasoningId) this.createReasoningBubble(this._lastSendText);

    // 1) 尚未进入 <think>：先找起始
    if (!this._inReasoning) {
      const openIdx = s.indexOf('<think>');
      if (openIdx >= 0) {
        const before = this.cleanAnswer(s.slice(0, openIdx));
        if (before) this.appendAnswerInReasoning(before);

        this._inReasoning = true; // 进入 think
        s = s.slice(openIdx + '<think>'.length);
      }
    }

    // 2) 在 <think> 内部：尝试找闭合
    if (this._inReasoning) {
      const closeIdx = s.indexOf('</think>');
      if (closeIdx >= 0) {
        const thinkPart = s.slice(0, closeIdx);
        if (thinkPart) this.appendReasoningText(thinkPart);
        this._inReasoning = false;
        this.finishReasoning();
        const rest = this.cleanAnswer(s.slice(closeIdx + '</think>'.length));
        if (rest) this.appendAnswerInReasoning(rest);
        return;
      } else {
        if (s) this.appendReasoningText(s);
        return;
      }
    }

    // 3) 非 think 段：直接追加到回答区
    const ans = this.cleanAnswer(s);
    if (ans) this.appendAnswerInReasoning(ans);
  },

  normalizeThinkTags(s: string) {
    if (!s) return '';
    return String(s)
      .replace(/《\s*think\s*》/gi, '<think>')
      .replace(/《\s*\/\s*think\s*》/gi, '</think>')
      .replace(/【\s*think\s*】/gi, '<think>')
      .replace(/【\s*\/\s*think\s*】/gi, '</think>')
      .replace(/<\s*thinking\s*>/gi, '<think>')
      .replace(/<\s*\/\s*thinking\s*>/gi, '</think>')
      .replace(/<\|\s*think\s*\|>/gi, '<think>')
      .replace(/<\|\s*\/\s*think\s*\|>/gi, '</think>');
  },

  cleanAnswer(raw: string) {
    if (!raw) return '';
    let s = String(raw);
    s = s.replace(/^event\s*:\s*message\s*/i, ''); // 容错
    s = s.replace(/<[^>]+>/g, '');                 // 去 HTML 标签
    s = s.replace(/[\u0000-\u0008\u000B-\u000C\u000E-\u001F]/g, '');
    return s.trim();
  },

  ab2utf8(buf: ArrayBuffer) {
    const u8 = new Uint8Array(buf);
    let bin = ''; const CHUNK = 0x8000;
    for (let i = 0; i < u8.length; i += CHUNK) {
      const sub = u8.subarray(i, i + CHUNK);
      // @ts-ignore
      bin += String.fromCharCode.apply(0, sub as any);
    }
    try { return decodeURIComponent(escape(bin)); } catch { return bin; }
  },

  // —— 自动下滑：A/B 锚点交替 + 80ms 节流 —— 
  _scrollToBottom() {
    if (this._scrollTicking) return;
    this._scrollTicking = true;
    const next = this._flipToA ? 'bottomA' : 'bottomB';
    this._flipToA = !this._flipToA;
    this.setData({ scrollToId: next });
    setTimeout(() => { this._scrollTicking = false; }, 80);
  },

  // —— 操作条事件 —— //
  onCopy(e: WechatMiniprogram.TouchEvent) {
    const id = (e.currentTarget as any).dataset.id as string;
    const item = this.data.messages.find((m: any) => m.id === id) as ChatMsg | ReasoningMsg | undefined;
    if (!item) return;
    let text = '';
    if ((item as any).kind === 'reasoning') {
      text = (item as ReasoningMsg).answer || '';
    } else if ((item as any).role === 'assistant') {
      text = (item as any).text || '';
    }
    if (!text) { wx.showToast({ title: '没有可复制的内容', icon: 'none' }); return; }
    wx.setClipboardData({
      data: text,
      success: () => wx.showToast({ title: '已复制', icon: 'success' })
    });
  },

  onLike(e: WechatMiniprogram.TouchEvent) {
    const id = (e.currentTarget as any).dataset.id as string;
    const msgs = this.data.messages.slice();
    const idx = msgs.findIndex((m: any) => m.id === id && m.role === 'assistant');
    if (idx < 0) return;
    const m = msgs[idx] as any;
    if (m.like) {
      m.like = false;
    } else {
      m.like = true;
      m.dislike = false;
    }
    this.setData({ messages: msgs });
    // this.reportFeedback(id, m.like ? 'like' : 'dislike')
  },

  onDislike(e: WechatMiniprogram.TouchEvent) {
    const id = (e.currentTarget as any).dataset.id as string;
    const msgs = this.data.messages.slice();
    const idx = msgs.findIndex((m: any) => m.id === id && m.role === 'assistant');
    if (idx < 0) return;
    const m = msgs[idx] as any;
    if (m.dislike) {
      m.dislike = false;
    } else {
      m.dislike = true;
      m.like = false;
    }
    this.setData({ messages: msgs });
    // this.reportFeedback(id, m.dislike ? 'dislike' : 'like')
  },

  // —— 关键：重试覆盖原气泡 —— //
  onRetry(e: WechatMiniprogram.TouchEvent) {
    if (this._sending) return;

    const id = (e.currentTarget as any).dataset.id as string;
    const msgs = this.data.messages.slice();
    const idx = msgs.findIndex((m: any) => m.id === id && m.role === 'assistant');
    if (idx < 0) return;

    const msgAny = msgs[idx] as any;

    // 如果不是 reasoning，将其“就地升级”为 reasoning 气泡以统一处理
    if (msgAny.kind !== 'reasoning') {
      msgAny.kind = 'reasoning';
      msgAny.status = 'connecting';
      msgAny.text = '';            // <think> 内容
      msgAny.answer = msgAny.text || ''; // 旧的纯文本当作 answer 已显示过，重试要清空
      msgAny.open = true;
      msgAny.like = !!msgAny.like;
      msgAny.dislike = !!msgAny.dislike;
      msgAny.canRetry = false;
      msgAny.sourceUserText = msgAny.sourceUserText || this._lastSendText;
    }

    const question = (msgAny as ReasoningMsg).sourceUserText || this._lastSendText || '';
    if (!question) { wx.showToast({ title: '缺少原提问，无法重试', icon: 'none' }); return; }

    // 清空旧内容，回到“连接中”
    (msgAny as ReasoningMsg).text = '';
    (msgAny as ReasoningMsg).answer = '';
    (msgAny as ReasoningMsg).status = 'connecting';
    (msgAny as ReasoningMsg).open = true;
    (msgAny as ReasoningMsg).canRetry = false;

    this.setData({ messages: msgs });

    // 内部状态重置，并“把写入目标指向这条气泡”
    if (this._req && typeof this._req.abort === 'function') { try { this._req.abort(); } catch {} }
    this._sending = true;
    this._buf = '';
    this._inReasoning = false;
    this._reasoningId = id; // ✅ 后续 handleAnswerDelta/append* 都写回这条消息

    // 发起新的流式请求（不新增用户消息、不创建新气泡）
    this.streamAsk(question);
  },

  reportFeedback(_msgId: string, _action: 'like' | 'dislike') {
    // 接入你后端即可：
    // wx.request({ url: '/api/feedback', method: 'POST', data: { msgId: _msgId, action: _action } })
  },
});
